﻿//Todo: make title and description read-only fields to the user.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Security.Principal;
using System.Drawing;

using System.Diagnostics;

using Microsoft.SharePoint;

namespace Microsoft.PKS
{
	#region " Managed Config List "

	public class ManagedConfigList
	{
		#region " String Literal Constants "

		public const string listName = "PKS Configuration Settings";
		public const string listDesc = "This list stores the configuration settings for the Podcasting Kit for SharePoint";

		#endregion

		#region " Setup Default Values "

		public void ResetToDefaultSettings()
		{
			this.RemoveAll();

			this["Thumbnail.Size.Width"].Description = "Integer: Desired width of the thumbnail created by the thumbnail handler.";
			this["Thumbnail.Size.Width"].Value = "481";

			this["Thumbnail.Size.Height"].Description = "Integer: Desired height of the thumbnail created by the thumbnail handler.";
			this["Thumbnail.Size.Height"].Value = "360";

			this["Thumbnail.Position"].Value = "27%";
			this["Thumbnail.Position"].Description = "Percentage (Double): Desired position of the media file the grab the thumbnail from.";

			this["Thumbnail.AllowDistortion"].Description = "Boolean: When resizing the thumbnail, should distortion be allowed, or should borders be added to preserve the aspect ratio (True allows distortion, False adds borders to preserve the aspect ratio).";
			this["Thumbnail.AllowDistortion"].Value = "False";

			this["Thumbnail.BackgroundColor"].Description = "Color: Holds the color to use for the borders on undistorted thumbnails that require it.";
			this["Thumbnail.BackgroundColor"].Value = "Black";

			this["Thumbnail.OutputPath"].Value = @"C:\AcademyMobile\Thumbnails";
			this["Thumbnail.OutputPath"].Description = "This is where intermediate thumbnail files will be placed (required only for 64-bit platforms).";

			this["UploadOnBehalfOf.GroupName"].Description = "Group (String): Holds the name of the SharePoint user group that allows content author impersonation.";
			this["UploadOnBehalfOf.GroupName"].Value = "PKS Impersonators";

			this["ErrorHandling.InvalidMediaFile.Image"].Value = ""; 
			this["ErrorHandling.InvalidMediaFile.Image"].Description = "Image Uri (String): This image will be used as a thumbnail for invalid media files.";

			this["ErrorHandling.InvalidMediaFile.Message"].Value = "Invalid Media File";
			this["ErrorHandling.InvalidMediaFile.Message"].Description = "String: This message will be superimposed on to the thumbnail image for invalid media files.";

			this["ErrorHandling.InvalidMediaFile.Font"].Value = "Arial, 30, Black, White, Bold";
			this["ErrorHandling.InvalidMediaFile.Font"].Description = "Font (String): This is the font that will be used to write the error message (Format: Font Name, Size, Foreground Color, Background Color, Style)";
		
			this["ErrorHandling.NoVideoStream.Image"].Value = "";
			this["ErrorHandling.NoVideoStream.Image"].Description = "Image Uri (String): This image will be used as a thumbnail for valid media files that contain no video stream such as audio files.";

			this["ErrorHandling.NoVideoStream.Message"].Value = "Music File";
			this["ErrorHandling.NoVideoStream.Message"].Description = "String: This message will be superimposed on to the thumbnail image for valid media files that do not contain video streams.";

			this["ErrorHandling.NoVideoStream.Font"].Value = "Arial, 30, Black, White, Bold";
			this["ErrorHandling.NoVideoStream.Font"].Description = "Font (String): This is the font that will be used to write the error message (Format: Font Name, Size, Foreground Color, Background Color, Style)";
		}

		#endregion

		#region " Managed Config List Source "

		private SPList list = null;
		Dictionary<string, ManagedConfigListItem> itemLookup = new Dictionary<string, ManagedConfigListItem>(StringComparer.InvariantCultureIgnoreCase);

		public Guid Id
		{
			get
			{
				return list.ID;
			}
		}

		private bool enableCacheLookups = true;
		public bool EnableCacheLookups
		{
			get { return enableCacheLookups; }
			set
			{
				enableCacheLookups = value;
				foreach (KeyValuePair<string, ManagedConfigListItem> kvp in itemLookup)
				{
					kvp.Value.EnableCacheLookups = value;
				}
			}
		}

		private bool autoImpersonate = true;
		public bool AutoImpersonate
		{
			get { return autoImpersonate; }
			set
			{
				autoImpersonate = value;
				foreach (KeyValuePair<string, ManagedConfigListItem> kvp in itemLookup)
				{
					kvp.Value.AutoImpersonate = value;
				}
			}
		}

		//public ManagedConfigList(SPList list, bool enableCacheLookups): this(list)
		//{
		//    this.enableCacheLookups = enableCacheLookups;
		//}

		public ManagedConfigList(SPWeb web, bool enableCacheLookups)
			: this(web)
		{
			this.enableCacheLookups = enableCacheLookups;
		}

		//public ManagedConfigList(SPList list)
		//{
		//    if (list == null) { throw new ArgumentNullException("list"); }
		//    this.list = list;
		//}

		public ManagedConfigList(SPWeb web)
		{
			if (web == null) { throw new ArgumentNullException("web"); }

			SPSecurity.CodeToRunElevated func = delegate()
			{

				//find the list
				foreach (SPList list in web.Lists)
				{
					if (string.Equals(list.Title, listName, StringComparison.InvariantCultureIgnoreCase))
					{
						this.list = list;
						return;
					}
				}

				//The list does not exist, create it
				CreateList(web);
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }
		}

		private void CreateList(SPWeb web)
		{
			SPSecurity.CodeToRunElevated func = delegate()
			{

				//----------------------------------------------------------------------------------------------------
				//Create a new list
				Guid newListId = web.Lists.Add(listName, listDesc, SPListTemplateType.CustomGrid);
				foreach (SPList webList in web.Lists)
				{
					if (webList.ID == newListId)
					{
						list = webList;
					}
				}

				//----------------------------------------------------------------------------------------------------
				//Setup list properties
				list.EnableFolderCreation = false;
				list.NoCrawl = true;
				list.OnQuickLaunch = false;
				list.Fields.Add("Description", SPFieldType.Text, false);
				list.Fields.Add("Value", SPFieldType.Text, false);
				list.Update();

				//----------------------------------------------------------------------------------------------------
				//Setup permissions

				try
				{
					list.BreakRoleInheritance(false);

					//add site admins with full control, everybody not in the list get's access denied
					foreach (SPUser u in web.SiteAdministrators)
					{
						SPPrincipal p = u as SPPrincipal;
						SPRoleAssignment ra = new SPRoleAssignment(p);

						ra.RoleDefinitionBindings.Add(web.RoleDefinitions["Full Control"]);

						list.RoleAssignments.Add(ra);
					}
				}
				catch { }


				//----------------------------------------------------------------------------------------------------
				//Setup default view
				try
				{
					SPView view = list.DefaultView;
					view.ViewFields.Add(list.Fields["Value"]);
					view.ViewFields.Add(list.Fields["Description"]);

					if (view.ViewFields.Exists("DocIcon"))
					{
						SPField deleteMe = list.Fields.GetField("DocIcon");
						if (deleteMe != null)
						{
							view.ViewFields.Delete(deleteMe);
						}
					}

					if (view.ViewFields.Exists("Attachments"))
					{
						SPField deleteMe = list.Fields.GetField("Attachments");
						if (deleteMe != null)
						{
							view.ViewFields.Delete(deleteMe);
						}
					}

					view.Query = "<OrderBy><FieldRef Name=\"Title\" /></OrderBy>";

					view.Update();
				}
				catch { }

				//----------------------------------------------------------------------------------------------------
				//Put default values into the list
				ResetToDefaultSettings();
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }
		}

		private ManagedConfigListItem Add(string itemName)
		{
			if (string.IsNullOrEmpty(itemName)) { throw new ArgumentNullException("itemName"); }

			ManagedConfigListItem mcli = null;
			SPSecurity.CodeToRunElevated func = delegate()
			{

				SPItem i = list.Items.Add();
				i["Title"] = itemName;
				i.Update();

				mcli = new ManagedConfigListItem(i, this, enableCacheLookups);
				itemLookup[itemName] = mcli;
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }

			return mcli;
		}

		public void Remove(string itemName)
		{
			if (string.IsNullOrEmpty(itemName)) { throw new ArgumentNullException("itemName"); }

			SPSecurity.CodeToRunElevated func = delegate()
			{

				for (int i = list.Items.Count - 1; i >= 0; i--)
				{
					SPItem item = list.Items[i];
					if (string.Equals((string)item["Title"], itemName, StringComparison.InvariantCultureIgnoreCase))
					{
						item.Delete();
					}
				}

				if (itemLookup.ContainsKey(itemName)) { itemLookup.Remove(itemName); }
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }
		}

		public void RemoveAll()
		{
			SPSecurity.CodeToRunElevated func = delegate()
			{

				for (int i = list.Items.Count - 1; i >= 0; i--)
				{
					SPItem item = list.Items[i];
					item.Delete();
				}

				itemLookup.Clear();
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }
		}

		public bool Exists(string itemName)
		{
			bool exists = false;

			SPSecurity.CodeToRunElevated func = delegate()
			{

				if (enableCacheLookups)
				{
					if (itemLookup.ContainsKey(itemName))
					{
						ManagedConfigListItem ret = itemLookup[itemName];
						//has the item been renamed?
						if (!string.Equals(ret.Name, itemName, StringComparison.InvariantCultureIgnoreCase))
						{
							itemLookup.Remove(itemName);
							itemLookup.Add(ret.Name, ret);
						}
						else
						{
							exists = true;
							return;
						}
					}
				}

				//Couldn't find item in the cache
				foreach (SPItem item in list.Items)
				{
					if (string.Equals((string)item["Title"], itemName, StringComparison.InvariantCultureIgnoreCase))
					{
						itemLookup.Add(itemName, new ManagedConfigListItem(item, this, enableCacheLookups));
						exists = true;
						return;
					}
				}
			};

			if (autoImpersonate)
			{ SPSecurity.RunWithElevatedPrivileges(func); }
			else
			{ func.Invoke(); }

			return exists;
		}

		public ManagedConfigListItem this[string itemName]
		{
			get
			{
				ManagedConfigListItem ret = null;
				SPSecurity.CodeToRunElevated func = delegate()
				{

					if (string.IsNullOrEmpty(itemName)) { throw new ArgumentNullException("itemName"); }

					/*if (enableCacheLookups)
					{
						if (itemLookup.ContainsKey(itemName))
						{
							ret = itemLookup[itemName];

							//has the item been renamed?
							if (!string.Equals(ret.Name, itemName, StringComparison.InvariantCultureIgnoreCase))
							{
								itemLookup.Remove(itemName);
								itemLookup.Add(ret.Name, ret);
								ret = null;
							}
							else
							{
								return;
							}
						}
					}*/

					//Can't find the item in the list, let's take a look at what's here
					foreach (SPItem item in list.Items)
					{
						if (string.Equals(itemName, item["Title"].ToString(), StringComparison.InvariantCultureIgnoreCase))
						{
							ManagedConfigListItem mcli = new ManagedConfigListItem(item, this, enableCacheLookups);
							itemLookup[itemName] = mcli;
							ret = mcli;
							return;
						}
					}

					ret = Add(itemName);
				};

				if (autoImpersonate)
				{ SPSecurity.RunWithElevatedPrivileges(func); }
				else
				{ func.Invoke(); }

				return ret;
			}
		}

		#endregion
	}

	#endregion

	#region " Manged Config List Item "

	public class ManagedConfigListItem
	{
		private SPItem item = null;
		private ManagedConfigList parent = null;
		
		private int LastHit = int.MinValue;
		private void CheckCacheExpiration()
		{
			//if (LastHit == int.MinValue) { LastHit = Environment.TickCount; }

			//if ((Environment.TickCount - LastHit) > 5000) //cache values for 5 seconds max
			{
				//Trace.WriteLine("Destroying cached value for \"" + name + "\".");
				name = null;
				val = null;
				desc = null;
				LastHit = Environment.TickCount;
			}
		}

		public ManagedConfigList Parent { get { return parent; } }

		public int Id
		{
			get
			{
				return item.ID;
			}
		}
		
		private bool enableCacheLookups = true;
		public bool EnableCacheLookups
		{
			get { return enableCacheLookups; }
			set { enableCacheLookups = value; }
		}

		private bool autoImpersonate = true;
		public bool AutoImpersonate
		{
			get { return autoImpersonate; }
			set { autoImpersonate = value; }
		}

		public ManagedConfigListItem(SPItem item, ManagedConfigList parent, bool enableCacheLookups)
			: this(item, parent)
		{
			this.enableCacheLookups = enableCacheLookups;
		}

		public ManagedConfigListItem(SPItem item, ManagedConfigList parent)
		{
			if (item == null) { throw new ArgumentNullException("item"); }
			if (parent == null) { throw new ArgumentNullException("parent"); }

			this.parent = parent;
			this.item = item;
		}

		private string name = null;
		public string Name
		{
			get
			{
				CheckCacheExpiration();

				if ((name == null) || (!enableCacheLookups))
				{
					SPSecurity.CodeToRunElevated func = delegate()
					{
						name = (string)item["Title"];
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }

					if (name == null) { return string.Empty; }
				}
				
				return name;
			}
			set
			{
				CheckCacheExpiration();

				if ((!enableCacheLookups) || (name == null) || (name != value))
				{
					SPSecurity.CodeToRunElevated func = delegate()
					{

						if ((name != value) && (parent.Exists(value)))
						{
							parent.Remove(value); //prevents duplicate entries
						}

						name = value;
						if (name == null) { name = string.Empty; }
						item["Title"] = name;
						item.Update();
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }
				}
			}
		}

		string desc = null;
		public string Description
		{
			get
			{
				CheckCacheExpiration();

				if ((desc == null) || (!enableCacheLookups))
				{
					SPSecurity.CodeToRunElevated func = delegate()
					{
						desc = (string)item["Description"];
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }

					if (desc == null) { return string.Empty; }
				}
				return desc;
			}
			set
			{
				CheckCacheExpiration();

				if ((!enableCacheLookups) || (desc == null) || (desc != value))
				{
					SPSecurity.CodeToRunElevated func = delegate()
					{

						desc = value;
						if (desc == null) { desc = string.Empty; }
						item["Description"] = desc;
						item.Update();
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }
				}
			}
		}

		string val = null;
		public string Value
		{
			get
			{
				CheckCacheExpiration();

				if ((val == null) || (!enableCacheLookups))
				{
					Trace.WriteLine("Looking up value for [\"" + Name + "\"].Value");

					SPSecurity.CodeToRunElevated func = delegate()
					{
						val = (string)item["Value"];
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }

					if (val == null) { return string.Empty; }
				}
				else
				{
					Trace.WriteLine("Using cached value for [\"" + Name + "\"].Value");
				}

				return val;
			}
			set
			{
				CheckCacheExpiration();

				if ((!enableCacheLookups) || (val == null) || (val != value))
				{
					SPSecurity.CodeToRunElevated func = delegate()
					{

						val = value;
						if (val == null) { val = string.Empty; }
						item["Value"] = val;
						item.Update();
					};

					if (autoImpersonate)
					{ SPSecurity.RunWithElevatedPrivileges(func); }
					else
					{ func.Invoke(); }
				}
			}
		}


		public bool ValueAsBool(bool fallbackValue)
		{
			bool ret = fallbackValue;
			bool.TryParse(Value, out ret);
			return ret;
		}

		public int ValueAsInt(int fallbackValue)
		{
			int ret = fallbackValue;
			int.TryParse(Value, out ret);
			return ret;
		}

		public double ValueAsDouble(double fallbackValue)
		{
			double ret = fallbackValue;
			double.TryParse(Value, out ret);
			return ret;
		}

		public double ValueAsPercent(double fallbackValue)
		{
			if (string.IsNullOrEmpty(Value)) { return fallbackValue; }
			string v = Value.Replace("%", "");
			if (string.IsNullOrEmpty(v)) { return fallbackValue; }
			v = v.Trim();
			if (string.IsNullOrEmpty(v)) { return fallbackValue; }

			if (double.TryParse(v, out fallbackValue))
			{
				fallbackValue /= 100;
			}

			return fallbackValue;
		}


		private static string[] colorNames = null;
		private static Color ColorFromString(string input, Color fallbackValue)
		{
			if (colorNames == null)
			{
				colorNames = Enum.GetNames(typeof(KnownColor));
				for (int i = 0; i < colorNames.Length; i++)
				{
					colorNames[i] = colorNames[i].ToLower();
				}
			}

			if (string.IsNullOrEmpty(input)) { return fallbackValue; }
			input = input.ToLower().Trim();
			if (string.IsNullOrEmpty(input)) { return fallbackValue; }
			if (colorNames.Contains(input))
			{
				return Color.FromName(input);
			}

			try
			{
				input = input.Replace("#", "");
				if (string.IsNullOrEmpty(input)) { return fallbackValue; }

				if (input.Length == 3)	//#rgb format
				{
					int a = 255;
					int r = Convert.ToInt32(input.Substring(0, 1), 16);
					int g = Convert.ToInt32(input.Substring(1, 1), 16);
					int b = Convert.ToInt32(input.Substring(2, 1), 16);

					r = (r * 16) + r;
					g = (g * 16) + g;
					b = (b * 16) + b;

					return Color.FromArgb(a, r, g, b);
				}

				if (input.Length == 4)	//#argb format
				{
					int a = Convert.ToInt32(input.Substring(0, 1), 16);
					int r = Convert.ToInt32(input.Substring(1, 1), 16);
					int g = Convert.ToInt32(input.Substring(2, 1), 16);
					int b = Convert.ToInt32(input.Substring(3, 1), 16);

					a = (r * 16) + a;
					r = (r * 16) + r;
					g = (g * 16) + g;
					b = (b * 16) + b;

					return Color.FromArgb(a, r, g, b);
				}

				if (input.Length == 6)	//#rrggbb format
				{
					int a = 255;
					int r = Convert.ToInt32(input.Substring(0, 2), 16);
					int g = Convert.ToInt32(input.Substring(2, 2), 16);
					int b = Convert.ToInt32(input.Substring(4, 2), 16);

					return Color.FromArgb(a, r, g, b);
				}

				if (input.Length == 8)	//#aarrggbb format
				{
					int a = Convert.ToInt32(input.Substring(0, 2), 16);
					int r = Convert.ToInt32(input.Substring(2, 2), 16);
					int g = Convert.ToInt32(input.Substring(4, 2), 16);
					int b = Convert.ToInt32(input.Substring(6, 2), 16);

					return Color.FromArgb(a, r, g, b);
				}
			}
			catch { }
			return fallbackValue;
		}

		public Color ValueAsColor(Color fallbackValue)
		{
			return ColorFromString(Value, fallbackValue);
		}

		public GfxUtils.PowerFont ValueAsFont()
		{
			string v = Value;

			string fontName = "Arial";
			double fontSize = 20;
			Color foreColor = Color.White;
			Color outColor = Color.Black;
			string style = "";

			if (!string.IsNullOrEmpty(v))
			{ v = v.Trim(); }

			if (!string.IsNullOrEmpty(v))
			{
				string[] vs = v.Split(',');
				if (vs != null)
				{
					int idx = 0;
					
					if (vs.Length > idx)
					{
						if (!string.IsNullOrEmpty(vs[idx])) { vs[idx] = vs[idx].Trim(); }
						if (!string.IsNullOrEmpty(vs[idx])) { fontName = vs[idx]; }
					}

					idx = 1;
					if (vs.Length > idx)
					{
						if (!string.IsNullOrEmpty(vs[idx])) { vs[idx] = vs[idx].Trim(); }
						if (!string.IsNullOrEmpty(vs[idx])) { double.TryParse(vs[idx], out fontSize); }
					}

					idx = 2;
					if (vs.Length > idx)
					{ foreColor = ColorFromString(vs[idx], foreColor); }

					idx = 3;
					if (vs.Length > idx)
					{ outColor = ColorFromString(vs[idx], outColor); }

					idx = 4;
					if (vs.Length > idx)
					{
						style = vs[idx];
					}

				}
			}
			
			return new GfxUtils.PowerFont(fontName, (float)fontSize, foreColor, outColor, style);
		}


	}

	#endregion
}

